#include <gtest/gtest.h>
#include <gmock/gmock.h>

#include <Server.hpp>

#include "IOServicePoolMock.hpp"
#include "ConnectionFactoryMock.hpp"
#include "SignalMock.hpp"
#include "AcceptorMock.hpp"
#include "ConnectionMock.hpp"

using ::testing::InSequence;
using ::testing::Return;
using ::testing::Invoke;
using ::testing::_;

TEST(ServerTests, SimpleCreate)
{
	ConnectionFactoryMock connectionFactoryMock;
	SignalMock signalsMock;
	AcceptorMock acceptorMock;

	IIOServicePoolMock poolMock;

	{
		InSequence s;
		EXPECT_CALL(signalsMock, add(SIGINT)).Times(1);
		EXPECT_CALL(signalsMock, add(SIGTERM)).Times(1);
		#if defined(SIGQUIT)
			EXPECT_CALL(signalsMock, add(SIGQUIT)).Times(1);
		#endif

		EXPECT_CALL(signalsMock, asyncWait(_)).Times(1);
		EXPECT_CALL(acceptorMock, openBindAndListen("testhost", "testport")).Times(1);
		EXPECT_CALL(connectionFactoryMock, createConnection()).Times(1).WillOnce(Return(new ConnectionMock));
		EXPECT_CALL(acceptorMock, asyncAccept(_, _)).Times(1);
	}

	Server server("testhost", "testport", poolMock, signalsMock, acceptorMock, connectionFactoryMock);
}

TEST(ServerTests, HandleSignal)
{
	ConnectionFactoryMock connectionFactoryMock;
	SignalMock signalsMock;
	AcceptorMock acceptorMock;

	IIOServicePoolMock poolMock;

	{
		InSequence s;
		EXPECT_CALL(signalsMock, add(SIGINT)).Times(1);
		EXPECT_CALL(signalsMock, add(SIGTERM)).Times(1);
#if defined(SIGQUIT)
		EXPECT_CALL(signalsMock, add(SIGQUIT)).Times(1);
#endif

		EXPECT_CALL(signalsMock, asyncWait(_)).Times(1);
		EXPECT_CALL(acceptorMock, openBindAndListen("testhost", "testport")).Times(1);
		EXPECT_CALL(connectionFactoryMock, createConnection()).Times(1).WillOnce(Return(new ConnectionMock));
		EXPECT_CALL(acceptorMock, asyncAccept(_, _)).Times(1);
		EXPECT_CALL(poolMock, stop()).Times(1);
	}

	Server server("testhost", "testport", poolMock, signalsMock, acceptorMock, connectionFactoryMock);
	static_cast<ISignalHandler&>(server).handleSignal();
}

TEST(ServerTests, Run)
{
	ConnectionFactoryMock connectionFactoryMock;
	SignalMock signalsMock;
	AcceptorMock acceptorMock;

	IIOServicePoolMock poolMock;

	{
		InSequence s;
		EXPECT_CALL(signalsMock, add(SIGINT)).Times(1);
		EXPECT_CALL(signalsMock, add(SIGTERM)).Times(1);
#if defined(SIGQUIT)
		EXPECT_CALL(signalsMock, add(SIGQUIT)).Times(1);
#endif

		EXPECT_CALL(signalsMock, asyncWait(_)).Times(1);
		EXPECT_CALL(acceptorMock, openBindAndListen("testhost", "testport")).Times(1);
		EXPECT_CALL(connectionFactoryMock, createConnection()).Times(1).WillOnce(Return(new ConnectionMock));
		EXPECT_CALL(acceptorMock, asyncAccept(_, _)).Times(1);
		EXPECT_CALL(poolMock, run()).Times(1);
	}

	Server server("testhost", "testport", poolMock, signalsMock, acceptorMock, connectionFactoryMock);
	server.run();
}

TEST(ServerTests, onAcceptedWOError)
{
	ConnectionFactoryMock connectionFactoryMock;
	SignalMock signalsMock;
	AcceptorMock acceptorMock;

	IIOServicePoolMock poolMock;

	{
		InSequence s;
		EXPECT_CALL(signalsMock, add(SIGINT)).Times(1);
		EXPECT_CALL(signalsMock, add(SIGTERM)).Times(1);
#if defined(SIGQUIT)
		EXPECT_CALL(signalsMock, add(SIGQUIT)).Times(1);
#endif

		EXPECT_CALL(signalsMock, asyncWait(_)).Times(1);
		EXPECT_CALL(acceptorMock, openBindAndListen("testhost", "testport")).Times(1);
		
		ConnectionMock* connectionMock = new ConnectionMock();

		EXPECT_CALL(connectionFactoryMock, createConnection()).Times(1).WillOnce(Return(connectionMock));
		EXPECT_CALL(acceptorMock, asyncAccept(_, _)).Times(1);
		
		EXPECT_CALL(*connectionMock, start()).Times(1);

		EXPECT_CALL(connectionFactoryMock, createConnection()).Times(1).WillOnce(Return(new ConnectionMock()));
		EXPECT_CALL(acceptorMock, asyncAccept(_, _)).Times(1);
	}

	Server server("testhost", "testport", poolMock, signalsMock, acceptorMock, connectionFactoryMock);
	static_cast<IAcceptorHandler&>(server).onAccepted(boost::system::error_code());
}

TEST(ServerTests, onAcceptedWError)
{
	ConnectionFactoryMock connectionFactoryMock;
	SignalMock signalsMock;
	AcceptorMock acceptorMock;

	IIOServicePoolMock poolMock;

	{
		InSequence s;
		EXPECT_CALL(signalsMock, add(SIGINT)).Times(1);
		EXPECT_CALL(signalsMock, add(SIGTERM)).Times(1);
#if defined(SIGQUIT)
		EXPECT_CALL(signalsMock, add(SIGQUIT)).Times(1);
#endif

		EXPECT_CALL(signalsMock, asyncWait(_)).Times(1);
		EXPECT_CALL(acceptorMock, openBindAndListen("testhost", "testport")).Times(1);

		ConnectionMock* connectionMock = new ConnectionMock();

		EXPECT_CALL(connectionFactoryMock, createConnection()).Times(1).WillOnce(Return(connectionMock));
		EXPECT_CALL(acceptorMock, asyncAccept(_, _)).Times(1);

		EXPECT_CALL(*connectionMock, start()).Times(0);

		EXPECT_CALL(connectionFactoryMock, createConnection()).Times(1).WillOnce(Return(new ConnectionMock()));
		EXPECT_CALL(acceptorMock, asyncAccept(_, _)).Times(1);
	}

	Server server("testhost", "testport", poolMock, signalsMock, acceptorMock, connectionFactoryMock);
	static_cast<IAcceptorHandler&>(server).onAccepted(boost::system::error_code(1, boost::system::generic_category()));
}